﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Snake2
{
    class SnakeGameLogic
    {
        private readonly GameField gameField;
        private readonly Drawer drawer;
        private readonly Random rnd;
        private List<ConsoleSnake> snake;
        private ConsoleSnake target;
        private Direction currentDirection { get; set; }
        private InputHandler ctrl = new InputHandler();
        private int speed = 1;
        private int score;

        public SnakeGameLogic(GameField gameField, Drawer drawer)
        {
            this.gameField = gameField;
            this.drawer = drawer;
            rnd = new Random();

        }

        public void InitializeSnake()
        {
            snake = new List<ConsoleSnake>
            {
            new ConsoleSnake(gameField.X + 5, gameField.Y + 3, '@', ConsoleColor.Red),
            new ConsoleSnake (gameField.X + 4, gameField.Y + 3, '0', ConsoleColor.Yellow),
            new ConsoleSnake (gameField.X + 3, gameField.Y + 3, '0', ConsoleColor.Yellow)
            };

        }

        public void StartGame()
        {
            ctrl.KeyLeft += Left;
            ctrl.KeyUp += Up;
            ctrl.KeyRight += Right;
            ctrl.KeyDown += Down;
            ctrl.KeyP += Pause;
            int tempX;
            int tempY;

            bool isGameOver = false;
            for (;;)
            {
                Console.Clear();
                currentDirection = Direction.Right;
                InitializeSnake();
                target = GenerateRandomTarget();
                RandomPlacement(target);
                do
                {
                    ctrl.QueryKB();
                    tempX = snake[0].X;
                    tempY = snake[0].Y;
                    if (currentDirection == Direction.Right && CheckPosition(snake[0].X + 1, snake[0].Y) != Position.Body)
                    {
                        if (CheckPosition(snake[0].X + 1, snake[0].Y) == Position.Border)
                            snake[0].X = gameField.X - 1;
                        snake[0].MoveRight();
                    }
                    else if (currentDirection == Direction.Down && CheckPosition(snake[0].X, snake[0].Y + 1) != Position.Body)
                    {
                        if (CheckPosition(snake[0].X, snake[0].Y + 1) == Position.Border)
                            snake[0].Y = gameField.Y - 1;
                        snake[0].MoveDown();
                    }
                    else if (currentDirection == Direction.Left && CheckPosition(snake[0].X - 1, snake[0].Y) != Position.Body)
                    {
                        if (CheckPosition(snake[0].X - 1, snake[0].Y) == Position.Border)
                            snake[0].X = gameField.X + gameField.Width;
                        snake[0].MoveLeft();
                    }
                    else if (currentDirection == Direction.Up && CheckPosition(snake[0].X, snake[0].Y - 1) != Position.Body)
                    {
                        if (CheckPosition(snake[0].X, snake[0].Y-1) == Position.Border)
                            snake[0].Y = gameField.Y + gameField.Height;
                        snake[0].MoveUp();
                    }
                    else { isGameOver = true; }

                    if (isGameOver == false)
                    {
                        for (int i = snake.Count - 1; i > 1; i--)
                        {
                            snake[i].X = snake[i - 1].X;
                            snake[i].Y = snake[i - 1].Y;
                        }
                        snake[1].X = tempX;
                        snake[1].Y = tempY;
                    }

                    drawer.DrawGameField(gameField, '#');
                    drawer.DrawObject(target);
                    drawer.DrawObjectList(snake);
                    drawer.DrawScoreAndMessage(gameField, "", score, speed);

                    if (IsHeadOnTarget(snake[0]))
                    {
                        Console.WriteLine((char)7);
                        score = snake.Count - 2;
                        snake.Add(new ConsoleSnake(target.X, target.Y, '0', ConsoleColor.Yellow));
                        drawer.DrawScoreAndMessage(gameField, "", score, speed);
                        RandomPlacement(target);
                    }

                    if (score < 3)
                    {
                        Thread.Sleep(350);
                    }
                    else if (score < 6)
                    {
                        speed = 2;
                        Thread.Sleep(275);
                    }
                    else if (score < 9)
                    {
                        speed = 3;
                        Thread.Sleep(200);
                    }
                    else if (score < 12)
                    {
                        speed = 4;
                        Thread.Sleep(150);
                    }
                    else
                    {
                        Thread.Sleep(100);
                        speed = 5;
                    }
                    snake[snake.Count - 1].X = snake[snake.Count - 2].X;
                    snake[snake.Count - 1].Y = snake[snake.Count - 2].Y;
                } while (!isGameOver);
                drawer.DrawScoreAndMessage(gameField, "GAME OVER", score, speed);
                drawer.DrawObjectList(snake);
                Console.ReadKey();
                isGameOver = false;
                score = 0;
                speed = 1;
            }
        }

        private ConsoleSnake GenerateRandomTarget()
        {
            int randomX = 0;
            int randomY = 0;
            do
            {
                randomX = rnd.Next(gameField.X, gameField.Width + gameField.X);
                randomY = rnd.Next(gameField.Y, gameField.Height + gameField.Y);

            } while (!IsPositionFree(randomX, randomY));

            return new ConsoleSnake(randomX, randomY, (char)1, ConsoleColor.Green);
        }

        private bool IsPositionFree(int x, int y)
        {
            foreach (var obj in snake)
            {
                if (obj.IsOnPosition(x, y))
                    return false;
            }
            return true;
        }

        private bool IsHeadOnTarget(ConsoleSnake head)
        {
            if (head.X == target.X && head.Y == target.Y)
                return true;
            return false;
        }

        private void RandomPlacement(ConsoleSnake targ)
        {
            int x;
            int y;
            do
            {
                x = rnd.Next(gameField.X, gameField.Width + gameField.X);
                y = rnd.Next(gameField.Y, gameField.Height + gameField.Y);

            } while (!IsPositionFree(x, y));
            targ.X = x;
            targ.Y = y;
        }

        private Position CheckPosition(int x, int y)
        {
            if (!IsPositionFree(x, y))
                return Position.Body;
            if (!gameField.IsOnField(x, y))
                return Position.Border;
            return Position.Free;
        }

        public void Left(object sender, EventArgs e)
        {
            if (currentDirection != Direction.Right)
                currentDirection = Direction.Left;
        }
        public void Right(object sender, EventArgs e)
        {
            if (currentDirection != Direction.Left)
                currentDirection = Direction.Right;
        }
        public void Up(object sender, EventArgs e)
        {
            if (currentDirection != Direction.Down)
                currentDirection = Direction.Up;
        }
        public void Down(object sender, EventArgs e)
        {
            if (currentDirection != Direction.Up)
                currentDirection = Direction.Down;
        }
        public void Pause(object sender, EventArgs e)
        {
            drawer.DrawScoreAndMessage(gameField, "PAUSE", score, speed);
            ConsoleKeyInfo key;
            do
            {
                key = Console.ReadKey(true);
            }
            while (key.Key != ConsoleKey.P);
            drawer.DrawScoreAndMessage(gameField, "          ", score, speed);
        }
    }

    public enum Position
    {
        Body,
        Border,
        Free,
        Target
    }

    enum Direction
    {
        Up,
        Down,
        Left,
        Right
    }
}
